package com.tsing.cedap.rest.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tsing.cedap.common.config.RedisManager;
import com.tsing.cedap.common.constants.AuthConstant;
import com.tsing.cedap.common.exceptions.BusinessException;
import com.tsing.cedap.common.exceptions.CommonException;
import com.tsing.cedap.common.utils.GetLoginUserUtil;
import com.tsing.cedap.common.vo.PageResult;
import com.tsing.cedap.common.vo.UserVo;
import com.tsing.cedap.rest.entity.AccountEntity;
import com.tsing.cedap.rest.entity.OrderEntity;
import com.tsing.cedap.rest.entity.UserEntity;
import com.tsing.cedap.rest.entity.WalletEntity;
import com.tsing.cedap.rest.mapper.AccountMapper;
import com.tsing.cedap.rest.mapper.OrderMapper;
import com.tsing.cedap.rest.mapper.UserMapper;
import com.tsing.cedap.rest.mapper.WalletMapper;
import com.tsing.cedap.rest.service.IOrderService;
import com.tsing.cedap.rest.vo.reqVo.*;
import com.tsing.cedap.rest.vo.respVo.OrderRespVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author TheTsing
 * @create 2022-01-28 9:27
 */
@Service
public class OrderServiceImpl implements IOrderService {

    @Autowired
    private RedisManager redisManager;

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private WalletMapper walletMapper;

    @Resource
    private AccountMapper accountMapper;

    @Override
    public void placeOrder(PlaceOrderReqVo reqVo, HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        OrderEntity orderEntity = new OrderEntity();
        BeanUtil.copyProperties(reqVo, orderEntity);
        orderMapper.insert(orderEntity
                .setCustomerId(userVo.getId())
                .setOrderTime(new Date())
                .setStatus(1));
    }

    @Override
    public void updateTip(UpdateTipReqVo reqVo) {
        if (1 != new LambdaQueryChainWrapper<>(orderMapper)
                .select(OrderEntity::getStatus)
                .eq(OrderEntity::getId, reqVo.getId())
                .last("LIMIT 1")
                .one()
                .getStatus()) {
            throw new BusinessException(CommonException.Proxy.TIP_IS_NOT_ALLOW_CHANGE);
        }
        new LambdaUpdateChainWrapper<>(orderMapper)
                .eq(OrderEntity::getId, reqVo.getId())
                .set(OrderEntity::getTip, reqVo.getTip())
                .update();
    }

    @Override
    public void cancelOrder(CancelOrderReqVo reqVo) {
        Integer status = new LambdaQueryChainWrapper<>(orderMapper)
                .select(OrderEntity::getStatus)
                .eq(OrderEntity::getId, reqVo.getId())
                .last("LIMIT 1")
                .one()
                .getStatus();
        if (status != 1) {
            throw new BusinessException(CommonException.Proxy.ORDER_IS_NOT_ALLOW_CHANGE);
        }
        new LambdaUpdateChainWrapper<>(orderMapper)
                .eq(OrderEntity::getId, reqVo.getId())
                .set(OrderEntity::getCancelTime, new Date())
                .set(OrderEntity::getStatus, 0)
                .set(OrderEntity::getChangeReason, reqVo.getChangeReason())
                .update();
    }

    @Override
    public OrderRespVo getOrderById(CodeReqVo reqVo) {
        OrderRespVo orderRespVo = orderMapper.getOrderById(reqVo.getCode());
        if (StrUtil.isNotBlank(orderRespVo.getRiderId())) {
            UserEntity userEntity = new LambdaQueryChainWrapper<>(userMapper)
                    .select(UserEntity::getName, UserEntity::getPhone)
                    .eq(UserEntity::getId, orderRespVo.getRiderId())
                    .last("LIMIT 1")
                    .one();
            orderRespVo.setRiderName(userEntity.getName()).setRiderPhone(userEntity.getPhone());
        }
        return orderRespVo;
    }

    @Override
    public PageResult<OrderRespVo> orderPage(OrderPageReqVo reqVo, HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        IPage<OrderRespVo> page = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
        List<OrderRespVo> orderRespVos = orderMapper.orderPage(page, userVo.getId(), reqVo.getStatus());
        for (int i = 0; i < orderRespVos.size(); i++) {
            if (StrUtil.isNotBlank(orderRespVos.get(i).getRiderId())) {
                UserEntity userEntity = new LambdaQueryChainWrapper<>(userMapper)
                        .select(UserEntity::getName, UserEntity::getPhone)
                        .eq(UserEntity::getId, orderRespVos.get(i).getRiderId())
                        .last("LIMIT 1")
                        .one();
                orderRespVos.get(i).setRiderName(userEntity.getName()).setRiderPhone(userEntity.getPhone());
            }
        }
        return new PageResult<>(page, orderRespVos);
    }

    @Override
    public PageResult<OrderRespVo> missedOrdersPage(IdAndPageReqVo reqVo, HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        IPage<OrderRespVo> page = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
        List<OrderRespVo> orderRespVos = orderMapper.missedOrdersPage(page, reqVo.getCode(), userVo.getId());
        return new PageResult<>(page, orderRespVos);
    }

    @Override
    public void updateRider(UpdateRiderReqVo reqVo) {
        OrderEntity orderEntity = new LambdaQueryChainWrapper<>(orderMapper)
                .select(OrderEntity::getId)
                .eq(OrderEntity::getId, reqVo.getId())
                .eq(OrderEntity::getStatus, 1)
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isNull(orderEntity)) {
            throw new BusinessException(CommonException.Proxy.ORDER_IS_NOT_ALLOW_CHANGE);
        }
        new LambdaUpdateChainWrapper<>(orderMapper)
                .set(OrderEntity::getRiderId, reqVo.getDenialService() ? null : reqVo.getRiderId())
                .eq(OrderEntity::getId, reqVo.getId())
                .update();
    }

    @Override
    public void riderReceive(RiderReceiveReqVo reqVo) {
        OrderEntity orderEntity = new LambdaQueryChainWrapper<>(orderMapper)
                .select(OrderEntity::getId)
                .eq(OrderEntity::getId, reqVo.getId())
                .eq(OrderEntity::getStatus, 1)
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isNull(orderEntity)) {
            throw new BusinessException(CommonException.Proxy.ORDER_IS_NOT_ALLOW_CHANGE);
        }
        new LambdaUpdateChainWrapper<>(orderMapper)
                .set(OrderEntity::getRiderId, reqVo.getRiderId())
                .set(OrderEntity::getStatus, 2)
                .set(OrderEntity::getReceiveTime, new Date())
                .eq(OrderEntity::getId, reqVo.getId())
                .update();
    }

    @Override
    public String getQr(CodeReqVo reqVo) {
        OrderEntity orderEntity = new LambdaQueryChainWrapper<>(orderMapper)
                .select(OrderEntity::getId)
                .eq(OrderEntity::getId, reqVo.getCode())
                .eq(OrderEntity::getStatus, 2)
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isNull(orderEntity)) {
            throw new BusinessException(CommonException.Proxy.ORDER_IS_NOT_ALLOW_CHANGE);
        }
        String uuid = IdUtil.fastSimpleUUID();
        redisManager.setString(uuid, reqVo.getCode(), 60 * 30);
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("QrCode.png");
        return QrCodeUtil.generateAsBase64(uuid, new QrConfig(), ImgUtil.IMAGE_TYPE_PNG, IoUtil.readBytes(inputStream));
    }

    @Override
    public void verifyQr(CodeReqVo reqVo) {
        String id = redisManager.getString(reqVo.getCode());
        if (StrUtil.isBlank(id)) {
            throw new BusinessException(CommonException.Proxy.VERIFY_QR_ERROR);
        }
        OrderEntity orderEntity = new LambdaQueryChainWrapper<>(orderMapper)
                .select(OrderEntity::getId)
                .eq(OrderEntity::getId, id)
                .eq(OrderEntity::getStatus, 2)
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isNull(orderEntity)) {
            throw new BusinessException(CommonException.Proxy.ORDER_IS_NOT_ALLOW_CHANGE);
        }
        new LambdaUpdateChainWrapper<>(orderMapper)
                .set(OrderEntity::getStatus, 3)
                .set(OrderEntity::getSendTime, new Date())
                .eq(OrderEntity::getId, id)
                .update();
        redisManager.deleteString(reqVo.getCode());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void customerPay(CodeReqVo reqVo) {
        OrderEntity orderEntity = new LambdaQueryChainWrapper<>(orderMapper)
                .select(OrderEntity::getId, OrderEntity::getCustomerId, OrderEntity::getTip, OrderEntity::getRiderId)
                .eq(OrderEntity::getId, reqVo.getCode())
                .eq(OrderEntity::getStatus, 3)
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isNull(orderEntity)) {
            throw new BusinessException(CommonException.Proxy.ORDER_IS_NOT_ALLOW_CHANGE);
        }
        //修改订单信息
        new LambdaUpdateChainWrapper<>(orderMapper)
                .set(OrderEntity::getStatus, 4)
                .set(OrderEntity::getCompleteTime, new Date())
                .set(OrderEntity::getPaymentMethod, 1)
                .eq(OrderEntity::getId, reqVo.getCode())
                .update();
        //查询客户钱包信息
        WalletEntity customerWallet = new LambdaQueryChainWrapper<>(walletMapper)
                .select(WalletEntity::getId, WalletEntity::getId, WalletEntity::getBalance)
                .eq(WalletEntity::getUserId, orderEntity.getCustomerId())
                .last("LIMIT 1")
                .one();
        //查询骑手钱包信息
        WalletEntity riderWallet = new LambdaQueryChainWrapper<>(walletMapper)
                .select(WalletEntity::getId, WalletEntity::getId, WalletEntity::getBalance)
                .eq(WalletEntity::getUserId, orderEntity.getRiderId())
                .last("LIMIT 1")
                .one();
        walletMapper.updateById(customerWallet.setBalance(customerWallet.getBalance().subtract(orderEntity.getTip())));
        walletMapper.updateById(riderWallet.setBalance(riderWallet.getBalance().add(orderEntity.getTip())));
        accountMapper.insert(new AccountEntity()
                .setUserId(orderEntity.getCustomerId())
                .setAmount(orderEntity.getTip())
                .setType(2)
                .setDescription("订单" + orderEntity.getId() + " 支出 " + orderEntity.getTip() + " 元，余额 " + customerWallet.getBalance() + " 元")
                .setCreateTime(new Date()));
        accountMapper.insert(new AccountEntity()
                .setUserId(orderEntity.getRiderId())
                .setAmount(orderEntity.getTip())
                .setType(1)
                .setDescription("订单" + orderEntity.getId() + " 收入 " + orderEntity.getTip() + " 元，余额 " + riderWallet.getBalance() + " 元")
                .setCreateTime(new Date()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void riderPay(CodeReqVo reqVo) {
        OrderEntity orderEntity = new LambdaQueryChainWrapper<>(orderMapper)
                .select(OrderEntity::getId, OrderEntity::getTip, OrderEntity::getRiderId)
                .eq(OrderEntity::getId, reqVo.getCode())
                .eq(OrderEntity::getStatus, 3)
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isNull(orderEntity)) {
            throw new BusinessException(CommonException.Proxy.ORDER_IS_NOT_ALLOW_CHANGE);
        }
        new LambdaUpdateChainWrapper<>(orderMapper)
                .set(OrderEntity::getStatus, 4)
                .set(OrderEntity::getCompleteTime, new Date())
                .set(OrderEntity::getPaymentMethod, 2)
                .eq(OrderEntity::getId, reqVo.getCode())
                .update();
        accountMapper.insert(new AccountEntity()
                .setUserId(orderEntity.getRiderId())
                .setAmount(orderEntity.getTip())
                .setType(4)
                .setDescription("订单" + orderEntity.getId() + " 现金收入 " + orderEntity.getTip() + " 元")
                .setCreateTime(new Date()));
    }

    @Override
    public PageResult<OrderRespVo> riderOrderPage(OrderPageReqVo reqVo, HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        IPage<OrderRespVo> page = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
        List<OrderRespVo> orderRespVos = orderMapper.riderOrderPage(page, userVo.getId(), reqVo.getStatus());
        if (CollUtil.isNotEmpty(orderRespVos)) {
            UserEntity userEntity = new LambdaQueryChainWrapper<>(userMapper)
                    .select(UserEntity::getName, UserEntity::getPhone)
                    .eq(UserEntity::getId, orderRespVos.get(0).getRiderId())
                    .last("LIMIT 1")
                    .one();
            orderRespVos = orderRespVos.stream().map(e -> e.setRiderName(userEntity.getName()).setRiderPhone(userEntity.getPhone())).collect(Collectors.toList());
        }
        return new PageResult<>(page, orderRespVos);
    }

    @Override
    public PageResult<OrderRespVo> orderPageBySchool(OrderPageBySchoolReqVo reqVo, HttpServletRequest request) {
        IPage<OrderRespVo> page = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
        List<OrderRespVo> orderRespVos = orderMapper.orderPageBySchool(page, reqVo);
        for (int i = 0; i < orderRespVos.size(); i++) {
            if (StrUtil.isNotBlank(orderRespVos.get(i).getRiderId())) {
                UserEntity userEntity = new LambdaQueryChainWrapper<>(userMapper)
                        .select(UserEntity::getName, UserEntity::getPhone)
                        .eq(UserEntity::getId, orderRespVos.get(i).getRiderId())
                        .last("LIMIT 1")
                        .one();
                orderRespVos.get(i).setRiderName(userEntity.getName()).setRiderPhone(userEntity.getPhone());
            }
        }
        return new PageResult<>(page, orderRespVos);
    }
}
